#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   ServiceStatus.py
@Time    :   2022-10-23 23:00
@Author  :   坐公交也用券
@Version :   1.0
@Contact :   faith01238@hotmail.com
@Homepage : https://liumou.site
@Desc    :   服务状态管理 - 专注于服务的启动、停止、重启等状态管理
"""
import re
import shlex
from typing import Optional

from .Jurisdiction import Jurisdiction
from .Cmd import NewCommand
from .logger import ColorLogger
from .OsInfo import OsInfo
from os import path


def clean_service_name(service_name: str) -> str:
    """
    清理服务名称，移除多余的.service后缀并确保符合命名规范
    :param service_name: 原始服务名称
    :return: 清理后的服务名称
    """
    if not service_name or not isinstance(service_name, str):
        return service_name
    
    # 移除.service后缀
    if service_name.endswith('.service'):
        service_name = service_name[:-8]
    
    # 验证服务名称格式（只允许字母、数字、下划线和短划线）
    if not re.match(r'^[\w\-]+$', service_name):
        raise ValueError("Invalid service name '{}'. Only alphanumeric, underscore, and hyphen are allowed.".format(service_name))
    
    return service_name


class ServiceStatus:
    def __init__(self, service, password, log=True):
        """
        服务状态管理
        :param service: 服务名称
        :param password: 密码
        :param log: 是否开启文本日志
        """
        # 参数校验
        if not service or not isinstance(service, str):
            raise ValueError("服务名称必须是非空字符串")
        if not password or not isinstance(password, str):
            raise ValueError("密码必须是非空字符串")

        self.log = log
        self._password = self._secure_password(password)  # 安全存储密码
        self.service = service

        # 初始化日志
        log_file = self._get_log_file_path()
        try:
            self.logger = ColorLogger(file=log_file, txt=log, class_name=self.__class__.__name__)
        except Exception as e:
            raise RuntimeError(f"日志初始化失败: {e}")

        # 初始化权限和命令模块
        try:
            self.ju = Jurisdiction(passwd=self._password, logs=log)
            self.cmd = NewCommand(password=self._password, logs=log)
        except Exception as e:
            self.logger.error(f"初始化权限或命令模块失败: {e}")
            raise

    def _secure_password(self, password):
        """
        对密码进行安全处理
        :param password: 明文密码
        :return: 安全存储的密码
        """
        return password[::-1]

    def _get_log_file_path(self):
        """
        获取日志文件路径
        :return: 日志文件路径
        """
        try:
            home_dir = OsInfo().home_dir
            if not home_dir:
                raise ValueError("无法获取用户主目录")
            return path.join(home_dir, 'ServiceStatus.log')
        except Exception as e:
            raise RuntimeError(f"日志文件路径生成失败: {e}")

    def start(self, service=None, name=None):
        """
        启动服务
        :param service: 服务名称(英文)，仅允许字母、数字、下划线和短划线
        :param name: 服务名称(中文)，用于日志记录
        :return: 启动结果(bool)
        """
        # 校验 service 参数是否合法
        if service is None:
            service = self.service
        
        # 清理服务名称
        service = clean_service_name(service)

        # 设置默认的 name 值
        if name is None:
            name = service

        # 构造启动命令
        def build_command(service_name):
            return "systemctl start {}".format(service_name)

        try:
            # 执行命令并返回结果
            cmd = build_command(service)
            return self.cmd.sudo(cmd=cmd, name="Start {}".format(name))
        except Exception as e:
            # 捕获异常并记录日志
            self.logger.error("Failed to start service '{}': {}".format(service, e))
            return False

    def stop(self, service=None, name=None):
        """
        停止服务
        :param name: 服务名称(中文)
        :param service: 服务名称
        :return: 启动结果(bool)
        """
        # 检查输入参数
        if service is None and self.service is None:
            raise ValueError("参数 'service' 不能为空，且实例变量 'self.service' 未设置")
        
        # 清理服务名称
        if service is not None:
            service = clean_service_name(service)
        
        if name is None:
            name = service or self.service

        # 构造 systemctl 命令，使用参数化方式避免命令注入
        try:
            cmd_str = "systemctl stop {}".format(service or self.service)
            res = self.cmd.sudo(cmd=cmd_str, name="Stop %s" % name)
        except Exception as e:
            # 异常处理，记录错误日志并返回 False
            if self.log:
                self.logger.error("停止服务失败: {}, 错误信息: {}".format(name, e))
            return False

        # 日志记录
        if self.log:
            if res:
                self.logger.info("停止成功: {}".format(name))
            else:
                self.logger.error("停止失败: {}".format(name))

        return res

    def restart(self, service=None, name=None, reload=False):
        """
        重启服务
        :param reload: 是否重载服务配置
        :param name: 服务名称(中文)
        :param service: 服务名称
        :return: 启动结果(bool)
        """
        # 参数校验
        if service is None:
            service = self.service
        
        # 清理服务名称
        service = clean_service_name(service)
        
        if not isinstance(service, str) or not service.strip():
            raise ValueError("参数 'service' 必须为非空字符串")

        if name is None:
            name = service
        if not isinstance(name, str) or not name.strip():
            raise ValueError("参数 'name' 必须为非空字符串")

        if not isinstance(reload, bool):
            raise TypeError("参数 'reload' 必须为布尔值")

        try:
            # 重载服务配置（如果需要）
            if reload:
                c = "systemctl daemon-reload"
                res_reload = self.cmd.sudo(cmd=c, name='重载服务配置')
                if not res_reload and self.log:
                    self.logger.warning("重载服务配置失败，继续尝试重启服务")

            # 安全地构建命令字符串
            c = "systemctl restart {}".format(shlex.quote(service))
            res = self.cmd.sudo(cmd=c, name="Restart {}".format(name))

            # 日志记录
            if self.log:
                if res:
                    self.logger.info("重启成功: {}".format(name))
                else:
                    self.logger.error("重启失败: {}".format(name))

            return res

        except Exception as e:
            # 异常捕获与日志记录
            if self.log:
                self.logger.error("重启服务时发生错误: {}".format(e))
            return False

    def enable(self, service=None, name=None):
        """
        设置开机自启
        :param name: 服务名称(中文)
        :param service: 服务名称
        :return: 启动结果(bool)
        """
        # 确保 service 和 name 的值有效
        if service is None:
            if hasattr(self, 'service') and self.service is not None:
                service = self.service
            else:
                raise ValueError("Service name is required and cannot be None")

        # 清理服务名称
        service = clean_service_name(service)
        
        if name is None:
            name = service

        try:
            # 使用参数化方式构建命令，避免命令注入风险
            command = "systemctl enable {}".format(shlex.quote(service))
            result = self.cmd.sudo(cmd=command, name="Start {}".format(name))
            return result
        except Exception as e:
            # 捕获异常并返回更详细的错误信息
            self.logger.error("Error enabling service {}: {}".format(service, e))
            return False

    def disable(self, service=None, name=None):
        """
        关闭开机自启
        :param name: 服务名称(中文)
        :param service: 服务名称
        :return: 启动结果(bool)
        """
        try:
            # 检查 service 和 name 的默认值
            if service is None:
                if not hasattr(self, 'service') or self.service is None:
                    raise ValueError("Service name cannot be None")
                service = self.service
            
            # 清理服务名称
            service = clean_service_name(service)
            
            if name is None:
                name = service

            # 防止命令注入，使用参数化命令执行
            command = "systemctl disable {}".format(service)
            result = self.cmd.sudo(cmd=command, name="Disable %s" % name)

            return result

        except Exception as e:
            # 捕获异常并返回 False，同时记录错误信息
            print("Error occurred while disabling service {}: {}".format(name, e))
            return False

    def status(self, service=None, name=None):
        """
        检查服务状态
        :param name: 服务名称(中文)
        :param service: 服务名称
        :return: 状态字符串
        """
        # 参数校验
        if service is None:
            service = self.service
        
        # 清理服务名称
        service = clean_service_name(service)
        
        if not isinstance(service, str) or not service.strip():
            raise ValueError("参数 'service' 必须为非空字符串")

        if name is None:
            name = service
        if not isinstance(name, str) or not name.strip():
            raise ValueError("参数 'name' 必须为非空字符串")

        try:
            # 安全地构建命令字符串
            c = "systemctl status {}".format(shlex.quote(service))
            res = self.cmd.getout(cmd=c, name="Status {}".format(name))

            # 日志记录
            if self.log:
                self.logger.info("检查状态: {}".format(name))

            return res

        except Exception as e:
            # 异常捕获与日志记录
            if self.log:
                self.logger.error("检查服务状态时发生错误: {}".format(e))
            return ""

    def existence(self, service: Optional[str] = None, name: Optional[str] = None) -> bool:
        """
        判断服务是否存在
        :param name: 服务名称(中文)
        :param service: 服务名称,后面必须带service，例如：docker.service
        :return: 是否存在 (bool)
        """
        # 如果 service 未提供，则使用默认值
        if service is None:
            service = self.service
        
        # 确保服务名称包含.service后缀
        if not service.endswith('.service'):
            service = "{}.service".format(service)

        try:
            # 使用 systemctl list-units 获取所有服务信息，避免命令注入风险
            cmd = ["systemctl", "list-units", "--all", "--no-legend", "--no-pager"]
            res = self.cmd.getout_sudo(cmd=cmd, name=name)

            # 检查服务是否存在
            ok = any(line.strip().startswith(service) for line in res.splitlines())
        except Exception as e:
            # 捕获异常并记录日志，确保程序不会因异常崩溃
            if self.log and hasattr(self, 'logger'):
                self.logger.error("检查服务 {} 是否存在的过程中发生错误: {}".format(name, e))
            return False

        # 记录日志
        if self.log and hasattr(self, 'logger'):
            if ok:
                self.logger.info("服务存在: {}".format(name or service))
            else:
                self.logger.warning("服务不存在: {}".format(name or service))

        return ok